package svnservice;

import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collection;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;

import jobs.SVNService;
import models.Repository;

import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

import play.Logger;
import play.libs.Crypto;

public class SVNServiceImpl implements SVNService {
	
	private static final String FILE = "file:///";
	private SVNRepository repository = null;
	private boolean needToSetupFileFactory = true;
	private boolean needToSetupDAVFactory = true;
	private boolean needToSetupSVNFactory = true;
	
	public void connectToRepository(Repository repositoryToCheck) throws SVNException {
		if (repository!=null) {
			throw new IllegalStateException("Already connected to a repository");
		}
		if (repositoryToCheck.url.startsWith(FILE)) {
			if (needToSetupFileFactory) {
				Logger.info("Setting up the file factory");
				FSRepositoryFactory.setup();
				needToSetupFileFactory = false;
			}
		} else if (repositoryToCheck.url.startsWith("http")) {
			if (needToSetupDAVFactory) {
				Logger.info("Setting up the HTTP factory");
				DAVRepositoryFactory.setup();
				needToSetupDAVFactory=false;
			}
		} else if (repositoryToCheck.url.startsWith("svn")) {
			if (needToSetupSVNFactory) {
				Logger.info("Setting up the SVN factory");
				SVNRepositoryFactoryImpl.setup();
				needToSetupSVNFactory=false;
			}
		}
		
		final SVNURL connectionURL = SVNURL.parseURIDecoded(repositoryToCheck.url);
		repository = SVNRepositoryFactory.create(connectionURL);
		if (repositoryToCheck.url.startsWith("https")) {
			HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
				public boolean verify(final String hostName, final SSLSession session) {
					return true;
				}
			});
			final TrustManager[] trustAllCerts = new TrustManager[1];
			final TrustManager tm = new NonValidatingTrustManager();
			trustAllCerts[0] = tm;
			try {
				final SSLContext sc = javax.net.ssl.SSLContext.getInstance("SSL");
				sc.init(null, trustAllCerts, null);
				HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
			} catch (final NoSuchAlgorithmException nsae) {
				nsae.printStackTrace();
			} catch (final KeyManagementException kme) {
				kme.printStackTrace();
			}
			ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(repositoryToCheck.username, Crypto.decryptAES(repositoryToCheck.password));
			repository.setAuthenticationManager(authManager);
		}
	}

	public void disconnect() {
		if (repository==null) {
			throw new IllegalStateException("Already disconnected");
		}
		try {
			repository.closeSession();
		} finally {
			repository = null;
		}
		
	}

	public long getLatestRevisionForRepository(Repository repositoryToCheck) throws SVNException {
		return repository.getLatestRevision();
	}

	public SVNLogEntry getLogEntryForEntry(long i) throws SVNException, NoResultException {
		final String[] targetPaths = new String[0];
		@SuppressWarnings("unchecked")
		final Collection<SVNLogEntry> logEntries = repository.log(targetPaths, null, i, i, true, true);
		if (logEntries.size()==0) {
			throw new NoResultException(i);
		}
		return (SVNLogEntry)logEntries.iterator().next();
	}
	
	private static class NonValidatingTrustManager implements javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager {

		public void checkClientTrusted(X509Certificate[] arg0, String arg1)
				throws CertificateException {
			return;
		}

		public void checkServerTrusted(X509Certificate[] arg0, String arg1)
				throws CertificateException {
			return;
		}

		public X509Certificate[] getAcceptedIssuers() {
			return null;
		}
		
	}

}
